home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / FetchNews 1.0.0b / source / TCPConnections.unit < prev    next >
Encoding:
Text File  |  1993-01-16  |  17.5 KB  |  643 lines  |  [TEXT/PJMM]

  1. unit TCPConnections;
  2.  
  3. { TCPConnections © Peter Lewis, Oct 1991 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         TCPStuff;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.  
  13.     const
  14.         any_connection = 0;    { Pass to GetConnectionEvent }
  15.         no_connection = -1;    { Guaranteed invalid connection }
  16.  
  17.     type
  18.         connectionIndex = longInt;
  19.         connectionEvent = (C_NoEvent, C_Found, C_SearchFailed, C_NameFound, C_NameSearchFailed,{}
  20.             C_Established, C_FailedToOpen, C_Closing, C_Closed, C_CharsAvailable, C_HeartBeat);
  21.         connectionEventRecord = record
  22.                 event: connectionEvent;
  23.                 connection: connectionIndex;
  24.                 tcpc: TCPConnectionPtr;
  25.                 dataptr: ptr;
  26.                 value: longInt;
  27.                 timedout: boolean;
  28.             end;
  29.  
  30.     function InitConnections: OSErr;
  31.     procedure CloseConnections;
  32.     procedure TerminateConnections;
  33.     function CanQuit: boolean;
  34. { After Terminate, keep calling GetConnectionEvent(any_connection,cer) until CanQuit is true, then Finish }
  35.     procedure FinishConnections;
  36.     procedure FinishEverything;  { Or just call FinishEverything }
  37.     function FindAddress (var cp: connectionIndex; hostName: str255; dataptr: univ ptr): OSErr;
  38.     function FindName (var cp: connectionIndex; hostIP: longInt; dataptr: univ ptr): OSErr;
  39.     procedure FindString (hostIP: longInt; var s: str255);
  40.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  41.     function NewActiveConnection (var cp: connectionIndex; buffersize: longInt; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  42.     procedure CloseConnection (cp: connectionIndex);
  43.     procedure AbortConnection (cp: connectionIndex); { Violently close connection }
  44.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  45. { Pass any_connection for any event, otherwise cp specifies the event }
  46.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ ptr);
  47.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ ptr);
  48.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longInt);
  49.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longInt);
  50.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  51.     procedure SetHeartBeat (cp: connectionIndex; n: longInt); { Send C_HeartBeat every n ticks, 0 disables heartbeat }
  52.  
  53. implementation
  54.  
  55.     const
  56.         TCPCMagic = 'TCPC';
  57.         TCPCBadMagic = 'badc';
  58.  
  59.     const  { Tuning parameters }
  60.         max_connections = 64;
  61.         TO_FindAddress = 40 * 60;
  62.         TO_FindName = 40 * 60;
  63.         TO_ActiveOpen = 20 * 60;
  64.         TO_Closing = longInt(2) * 60 * 60;
  65.         TO_PassiveOpen = longInt(10) * 365 * 24 * 3600 * 60;  { Ten years should be safe enough right? :-) }
  66.  
  67.     type
  68.         myHostInfo = record
  69.                 hi: hostInfo;
  70.                 done: signedByte;
  71.             end;
  72.         myHostInfoPtr = ^myHostInfo;
  73.         statusType = (CS_None, CS_Searching, CS_NameSearching, CS_Opening, CS_Established, CS_Closing);
  74.         connectionRecord = record
  75.                 magic: OSType;
  76.                 conmagic: longInt;
  77.                 tcpc: TCPConnectionPtr;
  78.                 laststate: TCPStateType; { DEBUG }
  79.                 status: statusType;
  80.                 cacheFaultReturnP: myHostInfoPtr;
  81.                 closedone: boolean;
  82.                 timeout: longInt;
  83.                 dataptr: ptr;
  84.                 heartbeat: longInt; { Time for next heartbeat }
  85.                 period: longInt; { Ticks per heartbeat }
  86.                 tcpstates: packed array[TCPStateType] of boolean; { DEBUG }
  87.                 constates: packed array[connectionEvent] of boolean; { DEBUG }
  88.             end;
  89.  
  90.     var
  91.         connections: array[1..max_connections] of connectionRecord;
  92.         connectionItem: connectionIndex;
  93.         dnrptr: ptr;
  94.         connectionmagic: longInt;
  95.  
  96.     function ValidConnectionSafe (var cp: connectionIndex): boolean;
  97.         var
  98.             ocp: longInt;
  99.             vc: boolean;
  100.     begin
  101.         vc := false;
  102.         ocp := cp;
  103.         cp := cp mod (max_connections + 1);
  104.         if cp > 0 then
  105.             if connections[cp].magic = TCPCMagic then
  106.                 if connections[cp].conmagic = ocp then
  107.                     vc := true;
  108.         ValidConnectionSafe := vc;
  109.     end;
  110.  
  111.     function ValidConnection (var cp: connectionIndex): boolean;
  112.         var
  113.             vc: boolean;
  114.     begin
  115.         vc := ValidConnectionSafe(cp);
  116.         if not vc then
  117.             DebugStr('Invalid Connection');
  118.         ValidConnection := vc;
  119.     end;
  120.  
  121.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ ptr);
  122.     begin
  123.         if ValidConnection(cp) then
  124.             connections[cp].dataptr := dataptr;
  125.     end;
  126.  
  127.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ ptr);
  128.     begin
  129.         if ValidConnectionSafe(cp) then
  130.             dataptr := connections[cp].dataptr
  131.         else
  132.             dataptr := nil;
  133.     end;
  134.  
  135.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longInt);
  136.     begin
  137.         if ValidConnection(cp) then
  138.             connections[cp].timeout := timeout;
  139.     end;
  140.  
  141.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longInt);
  142.     begin
  143.         if ValidConnection(cp) then
  144.             timeout := connections[cp].timeout
  145.         else
  146.             timeout := -1;
  147.     end;
  148.  
  149.     procedure SetHeartBeat (cp: connectionIndex; n: longInt); { Send C_HeartBeat every n ticks }
  150.     begin
  151.         if ValidConnection(cp) then begin
  152.             if (n < 1) or (n = maxLongInt) then begin
  153.                 connections[cp].period := maxLongInt;
  154.                 connections[cp].heartbeat := maxLongInt;
  155.             end
  156.             else begin
  157.                 connections[cp].period := n;
  158.                 connections[cp].heartbeat := TickCount + n;
  159.             end;
  160.         end;
  161.     end;
  162.  
  163.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  164.     begin
  165.         if ValidConnectionSafe(cp) then
  166.             tcpc := connections[cp].tcpc
  167.         else
  168.             tcpc := nil;
  169.     end;
  170.  
  171.     function MyTCPState (con: TCPConnectionPtr): TCPStateType;
  172.     begin
  173.         if con = nil then
  174.             MyTCPState := T_Closed
  175.         else
  176.             MyTCPState := TCPState(con);
  177.     end;
  178.  
  179. {$S Init}
  180.     function InitConnections: OSErr;
  181.         var
  182.             oe, ooe: OSErr;
  183.             i: connectionIndex;
  184.     begin
  185.         for i := 1 to max_connections do
  186.             connections[i].magic := TCPCBadMagic;
  187.         connectionmagic := 0;
  188.         connectionItem := 1;
  189.         oe := TCPInit;
  190.         if oe = noErr then begin
  191.             oe := TCPOpenResolver(dnrptr);
  192.             if oe <> noErr then
  193.                 TCPFinish;
  194.         end;
  195.         InitConnections := oe;
  196.     end;
  197.  
  198. {$S Term}
  199.     procedure TerminateConnections;
  200.         var
  201.             i: connectionIndex;
  202.             oe: OSErr;
  203.     begin
  204.         for i := 1 to max_connections do
  205.             with connections[i] do
  206.                 if magic = TCPCMagic then
  207.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then
  208.                         if TCPState(tcpc) <> T_Closed then
  209.                             oe := TCPAbort(tcpc);
  210.     end;
  211.  
  212. {$S Term}
  213.     procedure CloseConnections;
  214.         var
  215.             i: connectionIndex;
  216.             oe: OSErr;
  217.     begin
  218.         for i := 1 to max_connections do
  219.             with connections[i] do
  220.                 if magic = TCPCMagic then
  221.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then
  222.                         if TCPState(tcpc) <> T_Closed then
  223.                             oe := TCPClose(tcpc, nil);
  224.     end;
  225.  
  226. {$S Term}
  227.     function CanQuit: boolean;
  228.         var
  229.             i: connectionIndex;
  230.     begin
  231.         CanQuit := true;
  232.         for i := 1 to max_connections do
  233.             if connections[i].magic = TCPCMagic then
  234.                 CanQuit := false;
  235.     end;
  236.  
  237. {$S Term}
  238.     procedure FinishConnections;
  239.     begin
  240.         TCPCloseResolver(dnrptr);
  241.         TCPFinish;
  242.     end;
  243.  
  244. {$S Term}
  245.     procedure FinishEverything;
  246.         var
  247.             cer: connectionEventRecord;
  248.             dummy: boolean;
  249.             er: eventrecord;
  250.             oe: OSErr;
  251.     begin
  252.         TerminateConnections;
  253.         while not CanQuit do begin
  254.             if GetConnectionEvent(any_connection, cer) then begin
  255.                 dummy := WaitNextEvent(everyEvent, er, 0, nil);
  256.             end
  257.             else
  258.                 dummy := WaitNextEvent(everyEvent, er, 5, nil);
  259.         end;
  260.         FinishConnections;
  261.     end;
  262.  
  263. {$S}
  264.     function CreateConnection (var cp: connectionIndex; dp: ptr): OSErr;
  265.         var
  266.             ts: TCPStateType;
  267.             ce: connectionEvent;
  268.     begin
  269.         connectionmagic := connectionmagic + max_connections + 1;
  270.         cp := 1;
  271.         while (connections[cp].magic = TCPCMagic) and (cp < max_connections) do
  272.             cp := cp + 1;
  273.         with connections[cp] do begin
  274.             if magic = TCPCMagic then
  275.                 CreateConnection := tooManyConnections
  276.             else begin
  277.                 magic := TCPCMagic;
  278.                 conmagic := cp + connectionmagic;
  279.                 closedone := false;
  280.                 tcpc := nil;
  281.                 status := CS_None;
  282.                 cacheFaultReturnP := nil;
  283.                 timeout := maxlongInt;
  284.                 dataptr := dp;
  285.                 period := maxLongInt;
  286.                 heartbeat := maxLongInt;
  287.                 CreateConnection := noErr;
  288.                 cp := cp + connectionmagic;
  289.  
  290.                 for ce := C_NoEvent to C_HeartBeat do
  291.                     constates[ce] := false;
  292.                 for ts := T_WaitingForOpen to T_Unknown do
  293.                     tcpstates[ts] := false;
  294.             end;
  295.         end;
  296.     end;
  297.  
  298.     procedure DestroyConnection (var cp: connectionIndex);
  299.     begin
  300.         if ValidConnection(cp) then
  301.             connections[cp].magic := TCPCBadMagic;
  302.         cp := -1;
  303.     end;
  304.  
  305.     function FindAddress (var cp: connectionIndex; hostName: str255; dataptr: univ ptr): OSErr;
  306.         var
  307.             oe: OSErr;
  308.             cpi: connectionIndex;
  309.     begin
  310.         oe := CreateConnection(cp, dataptr);
  311.         if oe = noErr then begin
  312.             cpi := cp;
  313.             if ValidConnection(cpi) then begin
  314.                 with connections[cpi] do begin
  315.                     cacheFaultReturnP := myHostInfoPtr(NewPtr(SizeOf(myHostInfo)));
  316.                     if cacheFaultReturnP = nil then
  317.                         oe := memFullErr
  318.                     else begin
  319.                         cacheFaultReturnP^.done := 0;
  320.                         oe := TCPStrToAddr(dnrptr, hostName, cacheFaultReturnP^.hi, cacheFaultReturnP^.done);
  321.                         if oe = cacheFault then begin
  322.                             timeout := TickCount + TO_FindAddress;
  323.                             oe := noErr;
  324.                         end
  325.                         else begin
  326.                             cacheFaultReturnP^.done := -1;
  327.                             cacheFaultReturnP^.hi.rtnCode := oe;
  328.                         end;
  329.                         status := CS_Searching;
  330.                     end;
  331.                     if oe <> noErr then begin
  332.                         if cacheFaultReturnP <> nil then
  333.                             DisposPtr(ptr(cacheFaultReturnP));
  334.                         DestroyConnection(cp);
  335.                     end;
  336.                 end;
  337.             end;
  338.         end;
  339.         FindAddress := oe;
  340.     end;
  341.  
  342.     procedure FindString (hostIP: longInt; var s: str255);
  343.     begin
  344.         TCPAddrToStr(dnrptr, hostIP, s);
  345.     end;
  346.  
  347.     function FindName (var cp: connectionIndex; hostIP: longInt; dataptr: univ ptr): OSErr;
  348.         var
  349.             oe: OSErr;
  350.             cpi: connectionIndex;
  351.     begin
  352.         oe := CreateConnection(cp, dataptr);
  353.         if oe = noErr then begin
  354.             cpi := cp;
  355.             if ValidConnection(cpi) then begin
  356.                 with connections[cpi] do begin
  357.                     cacheFaultReturnP := myHostInfoPtr(NewPtr(SizeOf(myHostInfo)));
  358.                     if cacheFaultReturnP = nil then
  359.                         oe := memFullErr
  360.                     else begin
  361.                         cacheFaultReturnP^.done := 0;
  362.                         oe := TCPAddrToName(dnrptr, hostIP, cacheFaultReturnP^.hi, cacheFaultReturnP^.done);
  363.                         if oe = cacheFault then begin
  364.                             timeout := TickCount + TO_FindName;
  365.                             oe := noErr;
  366.                         end
  367.                         else begin
  368.                             cacheFaultReturnP^.done := -1;
  369.                             cacheFaultReturnP^.hi.rtnCode := oe;
  370.                         end;
  371.                         status := CS_NameSearching;
  372.                     end;
  373.                     if oe <> noErr then begin
  374.                         if cacheFaultReturnP <> nil then
  375.                             DisposPtr(ptr(cacheFaultReturnP));
  376.                         DestroyConnection(cp);
  377.                     end;
  378.                 end;
  379.             end;
  380.         end;
  381.         FindName := oe;
  382.     end;
  383.  
  384.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longInt; localport: integer; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  385.         var
  386.             oe: OSErr;
  387.             cpi: connectionIndex;
  388.     begin
  389.         oe := CreateConnection(cp, dataptr);
  390.         if oe = noErr then begin
  391.             cpi := cp;
  392.             if ValidConnection(cpi) then
  393.                 with connections[cpi] do begin
  394.                     oe := TCPPassiveOpen(tcpc, buffersize, localPort, remotehost, remoteport, nil);
  395.                     timeout := TickCount + TO_PassiveOpen;
  396.                     status := CS_Opening;
  397.                     if oe <> noErr then
  398.                         DestroyConnection(cp);
  399.                 end;
  400.         end;
  401.         NewPassiveConnection := oe;
  402.     end;
  403.  
  404.     function NewActiveConnection (var cp: connectionIndex; buffersize: longInt; remotehost: longInt; remoteport: integer; dataptr: univ ptr): OSErr;
  405.         var
  406.             oe: OSErr;
  407.             cpi: connectionIndex;
  408.     begin
  409.         oe := CreateConnection(cp, dataptr);
  410.         if oe = noErr then begin
  411.             cpi := cp;
  412.             if ValidConnection(cpi) then
  413.                 with connections[cpi] do begin
  414.                     oe := TCPActiveOpen(tcpc, buffersize, 0, remotehost, remoteport, nil);
  415.                     timeout := TickCount + TO_ActiveOpen;
  416.                     status := CS_Opening;
  417.                     if oe <> noErr then
  418.                         DestroyConnection(cp);
  419.                 end;
  420.         end;
  421.         NewActiveConnection := oe;
  422.     end;
  423.  
  424.     procedure CloseConnection (cp: connectionIndex);
  425.         var
  426.             oe: OSErr;
  427.     begin
  428.         if ValidConnection(cp) then
  429.             with connections[cp] do begin
  430.                 if not closedone then begin
  431.                     if MyTCPState(tcpc) <> T_Closed then begin
  432.                         oe := TCPClose(tcpc, nil);
  433.                     end;
  434.                     closedone := true;
  435.                 end;
  436.                 status := CS_Closing;
  437.                 timeout := TickCount + TO_Closing;
  438.             end;
  439.     end;
  440.  
  441.     procedure AbortConnection (cp: connectionIndex);
  442.         var
  443.             oe: OSErr;
  444.     begin
  445.         if ValidConnection(cp) then
  446.             with connections[cp] do begin
  447.                 if MyTCPState(tcpc) <> T_Closed then
  448.                     oe := TCPAbort(tcpc);
  449.                 status := CS_Closing;
  450.                 timeout := TickCount + TO_Closing;
  451.             end;
  452.     end;
  453.  
  454.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  455.         procedure HandleConnection (cp: connectionIndex);
  456.             var
  457.                 oe: OSErr;
  458.                 dummysp: stringPtr;
  459.                 l: integer;
  460.                 rcp: connectionIndex;
  461.         begin
  462.             with connections[cp] do begin
  463.                 rcp := conmagic;
  464.                 cer.connection := rcp;
  465.                 cer.tcpc := tcpc;
  466.                 cer.dataptr := dataptr;
  467.                 cer.timedout := false;
  468.                 case status of
  469.                     CS_NameSearching: 
  470.                         with cacheFaultReturnP^, hi do begin
  471.                             if done <> 0 then begin
  472.                                 if rtnCode = noErr then begin
  473.                                     cer.event := C_NameFound;
  474.                                     SanitizeHostName(rtnHostName);
  475.                                     stringHandle(cer.value) := NewString(rtnHostName);
  476.                                 end
  477.                                 else begin
  478.                                     cer.event := C_NameSearchFailed;
  479.                                     cer.value := rtnCode;
  480.                                 end
  481.                             end
  482.                             else if TickCount > timeout then begin
  483.                                 cer.event := C_NameSearchFailed;
  484.                                 cer.value := 1;
  485.                                 cer.timedout := true;
  486.                             end;
  487.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  488.                                 if done <> 0 then  { If we timed out, then we'll just have to abandon this block.  Oh well }
  489.                                     DisposPtr(ptr(cacheFaultReturnP));
  490.                                 cacheFaultReturnP := nil;
  491.                                 DestroyConnection(rcp);
  492.                             end; {if}
  493.                         end; {with}
  494.                     CS_Searching: 
  495.                         with cacheFaultReturnP^, hi do begin
  496.                             if rtnCode = noErr then begin
  497.                                 cer.event := C_Found;
  498.                                 cer.value := addrs[1];
  499.                             end
  500.                             else if done <> 0 then begin
  501.                                 cer.event := C_SearchFailed;
  502.                                 cer.value := rtnCode;
  503.                             end
  504.                             else if TickCount > timeout then begin
  505.                                 cer.event := C_SearchFailed;
  506.                                 cer.value := 1;
  507.                                 cer.timedout := true;
  508.                             end;
  509.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  510.                                 if done <> 0 then  { If we timed out, then we'll just have to abandon this block.  Oh well }
  511.                                     DisposPtr(ptr(cacheFaultReturnP));
  512.                                 cacheFaultReturnP := nil;
  513.                                 DestroyConnection(rcp);
  514.                             end; {if}
  515.                         end; {with}
  516.                     CS_Opening:  begin
  517.                         laststate := MyTCPState(tcpc);
  518.                         tcpstates[laststate] := true;
  519.                         case laststate of
  520.                             T_WaitingForOpen, T_Opening, T_Listening: 
  521.                                 if TickCount > timeout then begin
  522.                                     CloseConnection(rcp);
  523.                                     cer.event := C_FailedToOpen;
  524.                                     cer.timedout := true;
  525.                                 end;
  526.                             T_Established:  begin
  527.                                 cer.event := C_Established;
  528.                                 status := CS_Established;
  529.                                 timeout := maxLongInt;
  530.                             end;
  531.                             T_PleaseClose, T_Closing:  begin
  532.                                 CloseConnection(rcp);
  533.                                 cer.value := 1;
  534.                                 cer.event := C_FailedToOpen;
  535.                                 timeout := TickCount + TO_Closing;
  536.                             end;
  537.                             T_Closed:  begin
  538.                                 status := CS_Closing;
  539.                                 cer.value := 2;
  540.                                 cer.event := C_FailedToOpen;
  541.                                 timeout := TickCount + TO_Closing;
  542.                             end;
  543.                             otherwise
  544.                                 ;
  545.                         end; {case }
  546.                     end;
  547.                     CS_Established:  begin
  548.                         laststate := MyTCPState(tcpc);
  549.                         tcpstates[laststate] := true;
  550.                         case laststate of
  551.                             T_Established:  begin
  552.                                 cer.value := TCPCharsAvailable(tcpc);
  553.                                 if cer.value > 0 then
  554.                                     cer.event := C_CharsAvailable;
  555.                             end;
  556.                             T_PleaseClose, T_Closing:  begin
  557.                                 cer.value := TCPCharsAvailable(tcpc);
  558.                                 if cer.value > 0 then
  559.                                     cer.event := C_CharsAvailable
  560.                                 else begin
  561. {    CloseConnection(rcp);}
  562.                                     status := CS_Closing;
  563.                                     cer.event := C_Closing;
  564.                                     timeout := TickCount + TO_Closing;
  565.                                 end;
  566.                             end;
  567.  
  568.                             T_Closed:  begin
  569.                                 status := CS_Closing;
  570.                                 cer.event := C_Closing;
  571.                                 timeout := TickCount + TO_Closing;
  572.                             end;
  573.                             otherwise
  574.                                 ;
  575.                         end;
  576.                     end;
  577.                     CS_Closing:  begin
  578.                         laststate := MyTCPState(tcpc);
  579.                         tcpstates[laststate] := true;
  580.                         case laststate of
  581.                             T_WaitingForOpen, T_Opening, T_Listening: 
  582.                                 DebugStr('Strange State 2');
  583.                             T_PleaseClose, T_Closing, T_Established:  begin
  584.                                 cer.value := TCPCharsAvailable(tcpc);
  585.                                 if cer.value > 0 then
  586.                                     cer.event := C_CharsAvailable
  587.                                 else if TickCount > timeout then begin
  588.                                     cer.event := C_Closed;
  589.                                     if tcpc <> nil then begin
  590. {DebugStr('Closing timeout, call Abort;g');}
  591.                                         oe := TCPAbort(tcpc);
  592.                                         oe := TCPRelease(tcpc);
  593.                                     end;
  594.                                     cer.timedout := true;
  595.                                     DestroyConnection(rcp);
  596.                                 end;
  597.                             end;
  598.                             T_Closed:  begin
  599.                                 cer.event := C_Closed;
  600.                                 if tcpc <> nil then
  601.                                     oe := TCPRelease(tcpc);
  602.                                 DestroyConnection(rcp);
  603.                             end;
  604.                             otherwise
  605.                                 ;
  606.                         end;
  607.                     end;
  608.                     otherwise
  609.                         ;
  610.                 end;
  611.  
  612.                 if (cer.event = C_NoEvent) & (TickCount > heartbeat) then begin
  613.                     cer.event := C_HeartBeat;
  614.                     heartbeat := TickCount + period;
  615.                 end;
  616.                 if cer.event <> C_NoEvent then
  617.                     constates[cer.event] := true;
  618.             end;{with}
  619.         end;{HandleConnection}
  620.         var
  621.             oci: connectionIndex;
  622.     begin
  623.         cer.event := C_NoEvent;
  624.         if cp <> any_connection then begin
  625.             if ValidConnection(cp) then
  626.                 HandleConnection(cp);
  627.         end
  628.         else begin
  629.             oci := connectionItem;
  630.             repeat
  631.                 if connections[connectionItem].magic = TCPCMagic then begin
  632.                     HandleConnection(connectionItem);
  633.                 end;{if}
  634.                 if connectionItem = max_connections then
  635.                     connectionItem := 1
  636.                 else
  637.                     connectionItem := connectionItem + 1;
  638.             until (oci = connectionItem) or (cer.event <> C_NoEvent);
  639.         end;{if}
  640.         GetConnectionEvent := cer.event <> C_NoEvent;
  641.     end;{GetConnectionEvent}
  642.  
  643. end.